﻿using Docx2Epub.Docx;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;

namespace Docx2Epub.Epub
{
    class EpubOpfWriter : WriterBase
    {
        private string filePath;
        private IEnumerable<DocChapter> chapters;
        private EPubMetadatas metaDatas;

        public EpubOpfWriter(string outputPath, IEnumerable<DocChapter> chapters, EPubMetadatas metadatas)
        {
            this.filePath = Path.Combine(outputPath, "OEBPS", "package.opf");
            this.chapters = chapters;
            this.metaDatas = metadatas;
        }

        internal void WriteOut()
        {
            XElement doc = XElement.Load(filePath);
            // create metadata -+*+-*+*+-*+-*+-*+*+*-+*-+*-+*-+*-+
            createMetadatas(doc);

            // create manifest -+*+-*+*+-*+-*+-*+*+*-+*-+*-+*-+*-+
            createManifest(doc);

            // TODO image, fonts... -+*+-*+*+-*+-*+-*+*+*-+*-+*-+*-+*-+

            // create spine -+*+-*+*+-*+-*+-*+*+*-+*-+*-+*-+*-+
            createSpine(doc);

            using (FileStream fs = new FileStream(filePath, FileMode.Create, FileAccess.Write))
            using (StreamWriter sr = new StreamWriter(fs))
                doc.Save(sr);
        }

        private void createMetadatas(XElement doc)
        {
            var metadata = doc.XPathSelectElement("/opf:metadata", nsmgr);
            metadata.Add(new XElement(dc + "identifier", new XAttribute("id", "BookId"), this.metaDatas.BookId));
            metadata.Add(new XElement(dc + "date", this.metaDatas.PublishDate.ToUniversalTime()));
            metadata.Add(new XElement(opf + "meta", new XAttribute("property", "dcterms:modified"), this.metaDatas.ModifiedDate.ToUniversalTime()));
            metadata.Add(new XElement(dc + "title", this.metaDatas.Title));

            if (!string.IsNullOrEmpty(this.metaDatas.Contributor))
                metadata.Add(new XElement(dc + "contributor", this.metaDatas.Contributor));

            if (!string.IsNullOrEmpty(this.metaDatas.Creator))
                metadata.Add(new XElement(dc + "creator", this.metaDatas.Creator));

            if (!string.IsNullOrEmpty(this.metaDatas.Language))
                metadata.Add(new XElement(dc + "language", this.metaDatas.Language));

            if (!string.IsNullOrEmpty(this.metaDatas.Rights))
                metadata.Add(new XElement(dc + "rights", this.metaDatas.Rights));

            if (!string.IsNullOrEmpty(this.metaDatas.Publisher))
                metadata.Add(new XElement(dc + "publisher", this.metaDatas.Publisher));

        }

        private void createSpine(XElement doc)
        {
            var spine = doc.XPathSelectElement("/opf:spine", nsmgr);
            // chapter
            foreach (var c in this.chapters.Select(c => convertToSpineItem(c)))
                spine.Add(c);
        }

        private void createManifest(XElement doc)
        {
            var manifest = doc.XPathSelectElement("/opf:manifest", nsmgr);
            // image
            string ext = Path.GetExtension(this.metaDatas.CoverImagePath).ToLower();
            var image = manifest.XPathSelectElement("opf:item[@properties='cover-image']", this.nsmgr);
            image.Attribute("href").Value = "images/cover" + ext;
            if (ext.Contains("jpg") || ext.Contains("jpeg"))
                image.Attribute("media-type").Value = "image/jpeg";
            else if (ext.Contains("png"))
                image.Attribute("media-type").Value = "image/png";

            // chapters
            foreach (var e in this.chapters.Select(c => convertToManifestItem(c)))
                manifest.Add(e);
        }

        private XElement convertToSpineItem(DocChapter chapter)
        {
            var chap = new XElement(opf + "itemref");
            chap.Add(new XAttribute("idref", chapter.ChapterId));
            return chap;
        }

        private XElement convertToManifestItem(DocChapter chapter)
        {
            var chap = new XElement(opf + "item");
            chap.Add(new XAttribute("id", chapter.ChapterId));
            chap.Add(new XAttribute("href", chapter.FileName));
            chap.Add(new XAttribute("media-type", "application/xhtml+xml"));
            return chap;
        }
    }
}
